package com.demo.miya.aop;

import com.demo.miya.utils.ThreadLocalUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

@Slf4j
@Aspect
@Configuration
public class EndPointAop {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping) " +
            "|| @annotation(org.springframework.web.bind.annotation.PostMapping) " +
            "|| @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void endPoint() {}

    @Before("endPoint()")
    public void process(JoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();

        Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
        for (Annotation declaredAnnotation : declaredAnnotations) {
            InvocationHandler handler = Proxy.getInvocationHandler(declaredAnnotation);
            Class fieldValue = (Class) getFieldValue(handler);

            if (PostMapping.class.getName().equals(fieldValue.getName())) {
                PostMapping postMapping = method.getAnnotation(PostMapping.class);
                ThreadLocalUtils.setEndPonit("POST", postMapping.value()[0]);
            } else if (GetMapping.class.getName().equals(fieldValue.getName())) {
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                ThreadLocalUtils.setEndPonit("GET", getMapping.value()[0]);
            } else if (RequestMapping.class.getName().equals(fieldValue.getName())) {
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                String requestMethod = Arrays.stream(requestMapping.method())
                        .findFirst()
                        .map(Enum::name)
                        .orElse(StringUtils.EMPTY);

                String requestMappingValue = StringUtils.EMPTY;
                if (requestMapping.value().length > 0) {
                    requestMappingValue = requestMapping.value()[0];
                }
                ThreadLocalUtils.setEndPonit(requestMethod, requestMappingValue);
            }
        }

        log.info("EndPointAop value:{}", ThreadLocalUtils.getEndPoint());
    }

    @SneakyThrows
    private Object getFieldValue(InvocationHandler handler) {
        Class<? extends InvocationHandler> handlerClass = handler.getClass();
        Field field = handlerClass.getDeclaredField("type");
        field.setAccessible(true);
        return field.get(handler);
    }
}
